// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package ssa

// loopRotate在开始时转换具有检查循环条件的循环
// 结束时使用检查循环条件执行循环。
// 这有助于循环避免额外的不必要的跳跃。
// None
// 循环：
// CMPQ。。。
// JGE出口
// ...
// JMP循环
// 出口：
// None
// JMP条目
// 循环：
// ...
// 条目：
// CMPQ。。。
// JLT环路
func loopRotate(f *Func) {
	loopnest := f.loopnest()
	if loopnest.hasIrreducible {
		return
	}
	if len(loopnest.loops) == 0 {
		return
	}

	idToIdx := make([]int, f.NumBlocks())
	for i, b := range f.Blocks {
		idToIdx[b.ID] = i
	}

	// 我们正在移动的一组块，按ID。
	move := map[ID]struct{}{}

	// 从块ID映射到应
	// 马上来。
	after := map[ID][]*Block{}

	// 检查每个循环头并决定是否要移动它。
	for _, loop := range loopnest.loops {
		b := loop.header
		var p *Block // b的循环前置器
		for _, e := range b.Preds {
			if e.b.Kind != BlockPlain {
				continue
			}
			if loopnest.b2l[e.b.ID] != loop {
				continue
			}
			p = e.b
		}
		if p == nil || p == b {
			continue
		}
		after[p.ID] = []*Block{b}
		for {
			nextIdx := idToIdx[b.ID] + 1
			if nextIdx >= len(f.Blocks) { // 达到功能结束（可能不可能？）
				break
			}
			nextb := f.Blocks[nextIdx]
			if nextb == p { // 下一个是原始循环前置器
				break
			}
			if loopnest.b2l[nextb.ID] == loop {
				after[p.ID] = append(after[p.ID], nextb)
			}
			b = nextb
		}
		// 交换b和p，以便在移动块时先处理p，再处理b。
		f.Blocks[idToIdx[loop.header.ID]] = p
		f.Blocks[idToIdx[p.ID]] = loop.header
		idToIdx[loop.header.ID], idToIdx[p.ID] = idToIdx[p.ID], idToIdx[loop.header.ID]

		// 在p后面放b。
		for _, b := range after[p.ID] {
			move[b.ID] = struct{}{}
		}
	}

	// 一次性将块移动到其目的地。
	// 这里我们依赖于循环头必须出现的事实
	// 在循环的其余部分之前。这取决于
	// 事实上，我们只识别可约循环。
	j := 0
	// 可以放置一些不属于回路的块
	// 在循环块之间。为了避免这些障碍
	// 如果要覆盖，请使用临时切片。
	newOrder := make([]*Block, 0, f.NumBlocks())
	for _, b := range f.Blocks {
		if _, ok := move[b.ID]; ok {
			continue
		}
		newOrder = append(newOrder, b)
		j++
		for _, a := range after[b.ID] {
			newOrder = append(newOrder, a)
			j++
		}
	}
	if j != len(f.Blocks) {
		f.Fatalf("bad reordering in looprotate")
	}
	f.Blocks = newOrder
}
